package cn.falcon.fmobi.console.controller;

import cn.falcon.dmp.po.BaseRptModel;
import cn.falcon.fmobi.admin.dao.dao.model.ResultSetList;
import cn.falcon.fmobi.biz.exception.AdvertiserValidateException;
import cn.falcon.fmobi.biz.service.CommonService;
import cn.falcon.fmobi.biz.service.CustomerService;
import cn.falcon.fmobi.biz.service.RegionService;
import cn.falcon.fmobi.biz.service.dmp.RptGdtStrategyCreativeService;
import cn.falcon.fmobi.biz.service.dmp.RptGdtUserAgeDailyService;
import cn.falcon.fmobi.biz.service.dmp.RptGdtUserGenderDailyService;
import cn.falcon.fmobi.biz.service.dmp.RptGdtUserGeoDailyService;
import cn.falcon.fmobi.dao.enumration.ReportType;
import cn.falcon.fmobi.dao.model.ReportListModel;
import cn.falcon.fmobi.biz.vo.ReportResultModel;
import cn.falcon.fmobi.console.common.RestfulJsonModelAndView;
import cn.falcon.fmobi.console.common.Result;
import cn.falcon.fmobi.console.constant.UrlConstant;
import cn.falcon.fmobi.console.exception.CallBackException;
import cn.falcon.fmobi.console.exception.ParamValidateException;
import cn.falcon.fmobi.console.requestparam.ReportParam;
import cn.falcon.fmobi.console.requestparam.ReportQueryParam;
import cn.falcon.fmobi.dao.interceptor.PageParameter;
import cn.falcon.fmobi.dao.model.MenuVo;
import cn.falcon.fmobi.dao.util.DataUtils;
import cn.falcon.fmobi.dao.util.DateUtil;
import cn.falcon.fmobi.dao.util.MathUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpSession;
import java.math.BigDecimal;
import java.util.*;

/**
 * 数据报告
 *
 * @author yujuan.zhang
 * @from 2017-10-18
 * @since V1.0
 */
@Controller
@RequestMapping(UrlConstant.REPORT_URL)
public class ReportCtrl extends BaseCtrl {

    @Autowired
    private CustomerService customerService;

    @Autowired
    private RptGdtStrategyCreativeService rptGdtStrategyCreativeService;

    @Autowired
    private RegionService regionService;

    @Autowired
    private RptGdtUserGeoDailyService rptGdtUserGeoDailyService;

    @Autowired
    private RptGdtUserGenderDailyService rptGdtUserGenderDailyService;

    @Autowired
    private RptGdtUserAgeDailyService rptGdtUserAgeDailyService;

    @Autowired
    private CommonService commonService;

    /**
     * 数据报告列表查询
     *
     * @param reportQueryParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView getReportList(ReportQueryParam reportQueryParam, HttpSession session)
        throws ParamValidateException {
        Integer agentId = getLoginUser(session) != null ? getLoginUser(session).getAgentId() : null;
        if (null == agentId || 0 == agentId) {
            // TODO 权限不足
            throw new CallBackException(AdvertiserValidateException.ErrorCode.ADVERTISER_NOT_AUTH.getValue(),
                AdvertiserValidateException.ErrorCode.ADVERTISER_NOT_AUTH.getDesc());
        }
        reportQueryParam.setAgentId(agentId);

        // 时间参数验证
        this.validateSearchDate(reportQueryParam.getStartDate(), reportQueryParam.getEndDate());

        PageParameter pageParameter = reportQueryParam.getPageInfo();

        // 查询广告主列表
        List<ReportListModel> advertiserReportList = customerService.getAdvertiserListByPageForReport(reportQueryParam.getSearchMap(), pageParameter);

        // 当id列表不为空 并且 查询时间早于今天 查询数据报告
        if (!DataUtils.isNull(advertiserReportList) &&
            DateUtil.getStartTime(reportQueryParam.getStartDate()).getTime() <=
                DateUtil.getStartTime(new Date()).getTime()) {

            // 组装id列表
            List<Integer> advertiserIds = new ArrayList<>();
            for (ReportListModel reportListModel : advertiserReportList) {
                advertiserIds.add(reportListModel.getId());
            }

            // 数据报告查询参数
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("startDate", reportQueryParam.getStartDate());
            paramMap.put("endDate", reportQueryParam.getEndDate());
            paramMap.put("uids", advertiserIds);

            // 查询数据报告并组装数据
            advertiserReportList = commonService.createRpt(paramMap, advertiserReportList, readUserRates(session));
        }

        return RestfulJsonModelAndView.buildJsonModelAndView(new ResultSetList<>(pageParameter.getTotalCount(), advertiserReportList));
    }

    /**
     * 省份数据报告
     *
     * @param reportParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "state")
    public ModelAndView getStateReport(ReportParam reportParam, HttpSession session) throws ParamValidateException {
        // 参数和权限验证
        this.validateReportParam(reportParam, session);

        // 查询省份列表
        Map<Integer, MenuVo> stateMap = regionService.getStateList();

        // 1. 未来一段时间 数据为0
        if (DateUtil.getStartTime(reportParam.getStartDate()).after(DateUtil.getStartTime(new Date()))) {
            ReportResultModel data = this.convertEmptyResult(stateMap);
            return RestfulJsonModelAndView.buildJsonModelAndView(data);
        }

        // 2. 省份数据报告
        // 2.1 省份id列表
        List<Integer> stateIdList = new ArrayList<>();
        for (Integer id : stateMap.keySet()) {
            stateIdList.add(id);
        }
        // 2.2 根据广告主id和省份id列表 查询每个省份在指定时间段的数据报告
        Map<Integer, BaseRptModel> reportMap = rptGdtUserGeoDailyService
            .getStateReportListByAdvertiserId(reportParam.getAdvertiserId(), stateIdList, reportParam.getStartDate(),
                reportParam.getEndDate());
        // 组装返回结果的list
        List<BaseRptModel> resultList = this.convertReportResultList(stateMap, reportMap, readUserRates(session));
        // PV倒叙排序
        this.sortListByPvDesc(resultList);
        // 设置返回结果
        ReportResultModel data = new ReportResultModel();
        data.setList(resultList);
        return RestfulJsonModelAndView.buildJsonModelAndView(data);
    }

    /**
     * 对list按照pv倒叙排序
     *
     * @param list
     */
    private void sortListByPvDesc(List<BaseRptModel> list) {
        Collections.sort(list, new Comparator<BaseRptModel>() {
            @Override
            public int compare(BaseRptModel o1, BaseRptModel o2) {
                if (o1.getPv() > o2.getPv()) {
                    return -1;
                } else if (o1.getPv() < o2.getPv()) {
                    return 1;
                } else {
                    return 0;
                }

            }
        });
    }

    /**
     * 性别数据报告
     *
     * @param reportParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "gender")
    public ModelAndView getGenderReport(ReportParam reportParam, HttpSession session) throws ParamValidateException {
        // 参数和权限验证
        this.validateReportParam(reportParam, session);
        // 性别列表
        Map<Integer, MenuVo> genderMap = commonService.getGenderList();
        // 未来一段时间 数据为0
        if (DateUtil.getStartTime(reportParam.getStartDate()).after(DateUtil.getStartTime(new Date()))) {
            ReportResultModel data = this.convertEmptyResult(genderMap);
            return RestfulJsonModelAndView.buildJsonModelAndView(data);
        }
        // 根据广告主id查询指定时间段的性别数据报告
        Map<Integer, BaseRptModel> reportMap = rptGdtUserGenderDailyService
            .getGenderReportListByAdvertiserId(reportParam.getAdvertiserId(), reportParam.getStartDate(),
                reportParam.getEndDate());
        // 组装返回list
        List<BaseRptModel> resultList = this.convertReportResultList(genderMap, reportMap, readUserRates(session));
        // PV倒序排序
        this.sortListByPvDesc(resultList);
        // 设置返回结果
        ReportResultModel data = new ReportResultModel();
        data.setList(resultList);
        return RestfulJsonModelAndView.buildJsonModelAndView(data);
    }

    /**
     * 年龄数据报告
     *
     * @param reportParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "age")
    public ModelAndView getAgeReport(ReportParam reportParam, HttpSession session) throws ParamValidateException {
        // 参数和权限验证
        this.validateReportParam(reportParam, session);

        // 年龄段列表
        Map<Integer, MenuVo> ageRangeMap = commonService.getAgeRangeList();

        // 未来一段时间 数据为0
        if (DateUtil.getStartTime(reportParam.getStartDate()).after(DateUtil.getStartTime(new Date()))) {
            ReportResultModel data = this.convertEmptyResult(ageRangeMap);
            return RestfulJsonModelAndView.buildJsonModelAndView(data);
        }

        // 根据广告主id查询指定时间段的年龄数据报告
        Map<Integer, BaseRptModel> reportMap = rptGdtUserAgeDailyService
            .getAgeReportListByAdvertiserId(reportParam.getAdvertiserId(), reportParam.getStartDate(),
                reportParam.getEndDate());
        // 组装返回的list
        List<BaseRptModel> resultList = this.convertReportResultList(ageRangeMap, reportMap, readUserRates(session));
        // 按照PV倒叙排序
        this.sortListByPvDesc(resultList);
        // 组装返回数据
        ReportResultModel data = new ReportResultModel();
        data.setList(resultList);
        return RestfulJsonModelAndView.buildJsonModelAndView(data);
    }

    /**
     * (推广计划 策略管理 创意管理页面) 订单日报
     *
     * @param reportParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "campaign/daily")
    public ModelAndView getCampaignDailyReport(ReportParam reportParam, HttpSession session)
        throws ParamValidateException {
        this.validateSearchDate(reportParam.getStartDate(), reportParam.getEndDate());
        if (null == reportParam.getCampaignId()) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "campaignId", "Result.err.miss.parameter");
        }
        // 结束时间不能晚于今天
        if (DateUtil.getStartTime(reportParam.getEndDate()).after(DateUtil.getStartTime(new Date()))) {
            throw new ParamValidateException(Result.ERR_INVALID_PARAMETER, "endDate", "Result.err.invalid.parameter");
        }
        // TODO 权限验证 广告主或者代理商下是否存在该订单

        Map<String, BaseRptModel> reportMap = rptGdtStrategyCreativeService
            .getCampaignDailyReportList(reportParam.getCampaignId(), reportParam.getStartDate(),
                reportParam.getEndDate());

        // 日期列表
        List<Date> days = DateUtil.getBetweenDates(reportParam.getStartDate(), reportParam.getEndDate());

        // 组装返回数据列表
        List<BaseRptModel> list = this.convertDailyReportList(days, reportMap);

        // 返回数据列表按照日期升序排序
        Collections.sort(list, new Comparator<BaseRptModel>() {
            @Override
            public int compare(BaseRptModel o1, BaseRptModel o2) {
                return DateUtil.stringToDate(o1.getDesc(), "yyyy-MM-dd")
                    .compareTo(DateUtil.stringToDate(o2.getDesc(), "yyyy-MM-dd"));
            }
        });

        ReportResultModel result = new ReportResultModel();
        result.setList(list);
        return RestfulJsonModelAndView.buildJsonModelAndView(result);
    }

    /**
     * (推广计划 策略管理 创意管理页面) 订单时报
     *
     * @param reportParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "campaign/hourly")
    public ModelAndView getCampaignHourlyReport(ReportParam reportParam, HttpSession session)
        throws ParamValidateException {
        // 参数验证
        if (null == reportParam.getCampaignId()) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "campaignId", "Result.err.miss.parameter");
        }
        if (null == reportParam.getStartDate()) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "startDate", "Result.err.miss.parameter");
        }
        // 查询时间不能晚于今天
        if (DateUtil.getStartTime(reportParam.getStartDate()).after(DateUtil.getStartTime(new Date()))) {
            throw new ParamValidateException(Result.ERR_INVALID_PARAMETER, "startDate", "Result.err.invalid.parameter");
        }

        // TODO 权限验证 广告主或者代理商下是否存在该订单

        // 小时报告列表
        Map<String, BaseRptModel> reportMap = rptGdtStrategyCreativeService
            .getCampaignHoursReportList(reportParam.getCampaignId(), reportParam.getStartDate());

        // 小时报告列表
        List<BaseRptModel> list = this.convertHourlyReportList(reportMap);

        ReportResultModel result = new ReportResultModel();
        result.setList(list);
        return RestfulJsonModelAndView.buildJsonModelAndView(result);
    }

    /**
     * 策略管理 策略日报
     *
     * @param reportParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "strategy/daily")
    public ModelAndView getStrategyDailyReport(ReportParam reportParam, HttpSession session)
        throws ParamValidateException {
        this.validateSearchDate(reportParam.getStartDate(), reportParam.getEndDate());
        if (null == reportParam.getStrategyId()) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "strategyId", "Result.err.miss.parameter");
        }
        // 结束时间不能晚于今天
        if (DateUtil.getStartTime(reportParam.getEndDate()).after(DateUtil.getStartTime(new Date()))) {
            throw new ParamValidateException(Result.ERR_INVALID_PARAMETER, "endDate", "Result.err.invalid.parameter");
        }
        // TODO 权限验证 广告主或者代理商下是否存在该订单

        Map<String, BaseRptModel> reportMap = rptGdtStrategyCreativeService
            .getStrategyDailyReportList(reportParam.getStrategyId(), reportParam.getStartDate(),
                reportParam.getEndDate());

        // 日期列表
        List<Date> days = DateUtil.getBetweenDates(reportParam.getStartDate(), reportParam.getEndDate());

        // 组装返回数据列表
        List<BaseRptModel> list = this.convertDailyReportList(days, reportMap);

        // 返回数据列表按照日期升序排序
        Collections.sort(list, new Comparator<BaseRptModel>() {
            @Override
            public int compare(BaseRptModel o1, BaseRptModel o2) {
                return DateUtil.stringToDate(o1.getDesc(), "yyyy-MM-dd")
                    .compareTo(DateUtil.stringToDate(o2.getDesc(), "yyyy-MM-dd"));
            }
        });

        return RestfulJsonModelAndView.buildJsonModelAndView(list);
    }

    /**
     * 策略管理 策略时报
     *
     * @param reportParam 查询参数
     * @param session
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "strategy/hourly")
    public ModelAndView getStrategyHourlyReport(ReportParam reportParam, HttpSession session)
        throws ParamValidateException {
        // 参数验证
        if (null == reportParam.getStrategyId()) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "strategyId", "Result.err.miss.parameter");
        }
        if (null == reportParam.getStartDate()) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "startDate", "Result.err.miss.parameter");
        }
        // 查询时间不能晚于今天
        if (DateUtil.getStartTime(reportParam.getStartDate()).after(DateUtil.getStartTime(new Date()))) {
            throw new ParamValidateException(Result.ERR_INVALID_PARAMETER, "startDate", "Result.err.invalid.parameter");
        }

        // TODO 权限验证 广告主或者代理商下是否存在该订单

        // 小时报告列表
        Map<String, BaseRptModel> reportMap = rptGdtStrategyCreativeService
            .getStrategyHoursReportList(reportParam.getStrategyId(), reportParam.getStartDate());

        List<BaseRptModel> list = this.convertHourlyReportList(reportMap);

        return RestfulJsonModelAndView.buildJsonModelAndView(list);
    }

    /**
     * 广告主数据报告
     * @param type daily 日报  hourly 时报 strategy 策略 promotionPlan 推广计划
     * @param reportParam
     * @return
     */
    @RequestMapping(method = RequestMethod.GET, value = "typeRpt/{type}")
    public ModelAndView getReport(@PathVariable("type")String type,ReportParam reportParam, HttpSession session)
        throws ParamValidateException {
        this.validateReportParam(reportParam,session);
        ReportType reportType = null;
        if(ReportType.HOURLY.getName().equals(type)) {
            reportType = ReportType.HOURLY;
        }
        if(ReportType.DAILY.getName().equals(type)) {
            reportType = ReportType.DAILY;
        }
        if(ReportType.STRATEGY.getName().equals(type)) {
            reportType = ReportType.STRATEGY;
        }
        if(ReportType.PROMOTION_PLAN.getName().equals(type)) {
            reportType = ReportType.PROMOTION_PLAN;
        }
        //TODO 区分平台
        List<BaseRptModel>  baseRptModels = rptGdtStrategyCreativeService.getReportByWhat(reportParam.getSearchMap(),reportType, readUserRates(session));
        ReportResultModel result = new ReportResultModel();
        result.setList(baseRptModels);
        //TODO 报告导出
        return RestfulJsonModelAndView.buildJsonModelAndView(result);
    }

    //======================================================================================//

    /***
     * 报告查询参数和权限验证
     *
     * @param reportParam 查询参数
     * @param session
     * @throws ParamValidateException
     */
    private void validateReportParam(ReportParam reportParam, HttpSession session) throws ParamValidateException {
        Integer agentId = getLoginUser(session) != null ? getLoginUser(session).getAgentId() : null;
        Integer advertiserId = getLoginUser(session) != null ? getLoginUser(session).getAdvertiserId() : null;

        // 广告主权限 设置查询参数中的广告主id
        if (null != advertiserId) {
            reportParam.setAdvertiserId(advertiserId);
        }
        // 代理商权限下
        if (null != agentId) {
            if (null == reportParam.getAdvertiserId() || 0 == reportParam.getAdvertiserId()) {
                throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "advertiserId",
                    "Result.err.miss.parameter");
            }
            // 代理商下不存在该广告主
            // TODO 权限不足
            if (!customerService.agentExistAdvertiser(agentId, reportParam.getAdvertiserId())) {
                throw new CallBackException(AdvertiserValidateException.ErrorCode.ADVERTISER_NOT_AUTH.getValue(),
                    AdvertiserValidateException.ErrorCode.ADVERTISER_AMOUNT.getDesc());
            }
        }
        // 参数验证
        this.validateSearchDate(reportParam.getStartDate(), reportParam.getEndDate());
    }

    /**
     * 验证查询时间
     *
     * @param startDate 查询开始时间
     * @param endDate   查询结束时间
     */
    private void validateSearchDate(Date startDate, Date endDate) throws ParamValidateException {
        if (null == startDate) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "startDate", "Result.err.miss.parameter");
        }
        if (null == endDate) {
            throw new ParamValidateException(Result.ERR_MISS_PARAMETER, "endDate", "Result.err.miss.parameter");
        }
        if (startDate.after(endDate)) {
            throw new ParamValidateException(Result.ERR_INVALID_PARAMETER, "startDate endDate",
                "Result.err.invalid.parameter");
        }
    }

    /***
     * 组装数据报告返回list
     *
     * @param integerMenuVoMap 基础数据map
     * @param rptModelMap 数据报告返回结果map
     * @param rate         溢价费率
     * @return
     */
    private List<BaseRptModel> convertReportResultList(Map<Integer, MenuVo> integerMenuVoMap,
        Map<Integer, BaseRptModel> rptModelMap, BigDecimal rate) {
        // 组装返回list
        List<BaseRptModel> resultList = new ArrayList<>();
        for (Integer id : integerMenuVoMap.keySet()) {
            BaseRptModel baseRptModel = new BaseRptModel();
            baseRptModel.setId(id);
            baseRptModel.setDesc(integerMenuVoMap.get(id).getName());
            if (!DataUtils.isNull(rptModelMap) && rptModelMap.containsKey(id)) {
                baseRptModel.setPv(rptModelMap.get(id).getPv());
                baseRptModel.setClick(rptModelMap.get(id).getClick());
                baseRptModel.setCost(
                    MathUtil.divBigDecimal(BigDecimal.valueOf(rptModelMap.get(id).getCost()), rate).doubleValue());
            }
            resultList.add(baseRptModel);
        }
        return resultList;
    }

    /**
     * 组装空数据返回结果
     *
     * @param integerMenuVoMap 基础数据map
     * @return
     */
    private ReportResultModel convertEmptyResult(Map<Integer, MenuVo> integerMenuVoMap) {
        List<BaseRptModel> resultList = this.convertReportResultList(integerMenuVoMap, null, null);
        ReportResultModel data = new ReportResultModel();
        data.setList(resultList);
        return data;
    }

    /**
     * 组装每天的数据列表
     *
     * @param days      日期列表
     * @param reportMap 数据报告列表
     * @return
     */
    private List<BaseRptModel> convertDailyReportList(List<Date> days, Map<String, BaseRptModel> reportMap) {
        List<BaseRptModel> list = new ArrayList<>();
        for (Date date : days) {
            if (reportMap.containsKey(DateUtil.DateToStr(date, "yyyy-MM-dd"))) {
                list.add(reportMap.get(DateUtil.DateToStr(date, "yyyy-MM-dd")));
            } else {
                BaseRptModel baseRptModel = new BaseRptModel();
                baseRptModel.setDesc(DateUtil.DateToStr(date, "yyyy-MM-dd"));
                list.add(baseRptModel);
            }
        }
        return list;
    }

    /**
     * 组装小时报告列表
     *
     * @param reportMap 数据报告列表
     * @return
     */
    private List<BaseRptModel> convertHourlyReportList(Map<String, BaseRptModel> reportMap) {
        List<BaseRptModel> list = new ArrayList<>();
        // 前端展示小时数 需要1-24
        for (int i = 1; i <= 24; i++) {
            if (reportMap.containsKey("" + i)) {
                list.add(reportMap.get("" + i));
            } else {
                BaseRptModel baseRptModel = new BaseRptModel();
                baseRptModel.setDesc("" + i);
                list.add(baseRptModel);
            }
        }
        return list;
    }

}
